using CfgTable;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.Logic;
using NetProtocol.Enum;
using NetProtocol.POD;
using RecastUnity;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class LevelPlayController : TController
    {
        public LevelPlayRegionController regionCtrl { get; private set; }

        public CfgLevel cfgLevel { get; private set; }
        public bool isLocal { get; private set; }
        public long levelId { get; private set; }

        #region data
        public DynLevelPlayer levelPlayer { get; private set; }
        public Dictionary<long, DynLevelPlayer> players { get; private set; } = new Dictionary<long, DynLevelPlayer>();
        public Dictionary<int, int> levelParams { get; private set; }
        public int customTimeHour { get; private set; } = -1;
        public int curTimeHour => customTimeHour != -1 ?
            customTimeHour : (regionCtrl.cfgRegionPack.cfgScene.DefaultTime != -1 ?
            regionCtrl.cfgRegionPack.cfgScene.DefaultTime : GameDataCenter.ClientDisplayTime.Hour);
        #endregion

        #region view 
        public LevelPermanentResource permanentResource { get; private set; }
        #endregion

        public Dictionary<int, LevelRegionPackConfig> gameplayConfigsForLocal { get; private set; }
        public Dictionary<int, byte[]> navFilesForLocal { get; private set; }
        public bool isLevelPlayStarted { get; private set; }
        public bool isSceneReady => regionCtrl != null && regionCtrl.isSceneReady;

        public bool InitLevelController(CfgLevel cfgLevel)
        {
            this.cfgLevel = cfgLevel;
            if (cfgLevel.Regions.Count <= 0)
            {
                LogGame.LogError("CfgLevel RegionInstance is null,levelId:" + cfgLevel.Id);
                return false;
            }

            ManagerCenter.MonoDriver.updateEvent += Update;

            MsgDispatcher.AddListener<EnterLevelPOD>(MsgEventType.Level_Enter, NotifyEnterLevel);
            MsgDispatcher.AddListener<LevelRegionPOD>(MsgEventType.Level_EnterRegion, NotifyEnterRegion);
            MsgDispatcher.AddListener<int, int>(MsgEventType.Level_NotifyLevelParamChange, NotifyLevelParamChange);
            MsgDispatcher.AddListener<int>(MsgEventType.Level_NotifyHeroFollowerChange, NotifyHeroFollowerChange);
            MsgDispatcher.AddListener<int>(MsgEventType.Level_NotifyLevelTimeShowChange, OnNotifyLevelTimeShowChange);
            return true;
        }


        /// <summary>
        /// 准备本地模式关卡配置
        /// </summary>
        public async UniTask<LevelPlayController> PrepareLocalLevel()
        {
            isLocal = true;
            //load配置
            bool isOK = await LoadLocalModelConfig();
            if (!isOK)
            {
                return null;
            }
            //设置加载回调
            LogicHelper.GetLevelRegionPackConfig = (id) => gameplayConfigsForLocal.TryGetValue(id, out var result) ? result : null;
            LogicHelper.InitNavMesh = (id) =>
            {
                if (navFilesForLocal.TryGetValue(id, out byte[] bytes))
                {
                    return RecastDll.RecastLoad(RecastDllID.LevelLogicLocal, bytes, bytes.Length, true);
                }
                return IntPtr.Zero;
            };
            return this;
        }

        private void NotifyEnterLevel(EnterLevelPOD level)
        {
            AsyncNotifyEnterLevel(level).Forget();
        }

        private async UniTaskVoid AsyncNotifyEnterLevel(EnterLevelPOD levelPOD)
        {
            if (levelPOD.cid != cfgLevel.Id)
            {
                throw new Exception("levelPOD.cid != cfgLevel.Id");
            }

            LevelRegionPOD regionPOD = levelPOD.region;
            int regionCid = regionPOD.cid;
            // 初始化player
            levelPlayer = DynLevelPlayer.CreateByPOD(levelPOD.player, regionCid);
            players.Add(levelPlayer.id, levelPlayer);
            foreach (LevelOtherPlayerPOD pod in levelPOD.otherPlayers)
            {
                var p = DynLevelPlayer.CreateByPOD(pod, regionCid);
                players.Add(p.id, p);
            }

            levelId = levelPOD.id;
            levelParams = levelPOD.levelParams;
            customTimeHour = levelPOD.timeHour;

            // 加载常驻资源
            permanentResource = new LevelPermanentResource();
            await permanentResource.LoadResource();
            await EnterRegion(regionPOD);
        }

        public void OnLevelPlayStart()
        {
            isLevelPlayStarted = true;
        }

        private void NotifyEnterRegion(LevelRegionPOD regionPOD)
        {
            EnterRegion(regionPOD).Forget();
        }

        private async UniTask EnterRegion(LevelRegionPOD regionPOD)
        {
            LeaveRegion();
            regionCtrl = AddSubController<LevelPlayRegionController>();
            await regionCtrl.EnterRegion(regionPOD);
            //mark task complete for IGameplaySceneArea.TeleportToRegion
            LevelPlayModule.Instance.CompleteChangeRegionTask(true);
        }

        private void LeaveRegion()
        {
            if (regionCtrl != null)
            {
                regionCtrl.interactCtrl.EndFsm();
                RemoveSubController(regionCtrl);
                regionCtrl = null;
            }
        }

        private void NotifyLevelParamChange(int key, int value)
        {
            levelParams[key] = value;
        }

        private void NotifyHeroFollowerChange(int id)
        {
            levelPlayer.ChangeFollower(id);
        }

        private void OnNotifyLevelTimeShowChange(int timeHour)
        {
            customTimeHour = timeHour;
            regionCtrl.sceneCtrl.ChangeSceneRenderTime(curTimeHour);
        }

        /// <summary>
        /// 加载所有非配置表的二进制独立玩法配置文件
        /// 仅本地模式适用，服务器模式按指定地区加载
        /// </summary>
        /// <returns></returns>
        private async UniTask<bool> LoadLocalModelConfig()
        {
            //加载地区实例配置
            Dictionary<int, CfgRegionInstance> cfgRegionsMap = new Dictionary<int, CfgRegionInstance>();
            foreach (var regionId in cfgLevel.Regions)
            {
                //load region
                var cfgRegion = TableCenter.regionInstance.Get(regionId);
                if (cfgRegion == null)
                {
                    LogGame.LogError($"Cfg RegionInstance is null,levelId[{cfgLevel}] regionId[{regionId}]");
                    return false;
                }

                cfgRegionsMap.Add(regionId, cfgRegion);
            }

            //加载独立玩法和环境配置
            List<UniTask> tasks = ListPool<UniTask>.Get();
            ResLoader resloader = ResLoader.Create();
            Dictionary<int, LevelRegionPackConfig> packConfigMap = new Dictionary<int, LevelRegionPackConfig>();
            Dictionary<int, byte[]> navConfigMap = new Dictionary<int, byte[]>();
            foreach (var kv in cfgRegionsMap)
            {
                var regionId = kv.Key;
                var cfg = kv.Value;
                //gameplay config
                tasks.Add(UniTask.Create(async () =>
                {
                    var pack = await LevelPlayUtil.LoadRegionCfg(cfg, resloader);
                    if (pack != null)
                    {
                        packConfigMap.Add(regionId, pack);
                    }
                }));
                //recast navigation config
                tasks.Add(UniTask.Create(async () =>
                {
                    CfgScene cfgLevelScene = TableCenter.scene.Get(cfg.SceneId);
                    var path = PathConstant.GetNavMeshCfg(cfgLevelScene.NavMesh);
                    var text = await resloader.LoadABAssetAwait<TextAsset>(path);
                    if (text == null)
                    {
                        LogGame.LogError("Load NavMesh failed");
                    }
                    else
                    {
                        navConfigMap.Add(regionId, text.bytes);
                    }
                }));
            }
            await UniTask.WhenAll(tasks);

            resloader.Put2Pool();
            ListPool<UniTask>.Put(tasks);

            if (packConfigMap.Count != cfgRegionsMap.Count || navConfigMap.Count != cfgRegionsMap.Count)
            {
                return false;
            }

            gameplayConfigsForLocal = packConfigMap;
            navFilesForLocal = navConfigMap;

            return true;
        }

        /// <summary>
        /// 更新战斗结算信息，由玩家自己选择是否查看
        /// </summary>
        /// <param name="settlementPOD"></param>
        public void UpdateBattleSettlementInfo(BattleSettlementPOD settlementPOD)
        {
            levelPlayer.AddBattleSettlementData(BattleSettlementPodToData(settlementPOD));
            levelPlayer.UpdateHeroAttrBattleChange(settlementPOD.statisticsPOD.units);
        }

        private BattleSettlementData BattleSettlementPodToData(BattleSettlementPOD settlementPOD)
        {
            BattleSettlementData data = new BattleSettlementData();
            data.resultType = settlementPOD.resultType;
            data.statisticsPods = settlementPOD.statisticsPOD.units;
            return data;
        }

        /// <summary>
        /// 清理指定hero的buff
        /// </summary>
        /// <param name="heroId"></param>
        public void ClearHeroBuff(long pid, long heroId)
        {
            if (players.TryGetValue(pid, out var dynPlayer))
            {
                if (dynPlayer.levelHeros.TryGetValue(heroId, out var hero))
                {
                    hero.buffHolder.Clear();
                }
            }
        }

        /// <summary>
        /// 同步英雄战斗属性
        /// </summary>
        /// <param name="pid"></param>
        /// <param name="heroAttrs"></param>
        public void SyncHeroAttr(long pid, List<LevelHeroAttrSyncPOD> heroAttrs, AttrChangeReason reason)
        {
            if (players.TryGetValue(pid, out var dynPlayer))
            {
                foreach (var pod in heroAttrs)
                {
                    if (dynPlayer.levelHeros.TryGetValue(pod.id, out var hero))
                    {
                        var oldHp = hero.hp;
                        var oldSp = hero.sp;
                        hero.RefreshHpSp(pod.hp, pod.sp);
                        if (hero.id == levelPlayer.leaderHeroId) //本人英雄队长特殊处理
                        {
                            var addHp = pod.hp - oldHp;
                            var addSp = pod.sp - oldSp;
                            MsgDispatcher.Broadcast(MsgEventType.Level_LeaderHeroAddHpSp, hero.id, addHp, addSp, reason);
                        }
                    }
                }
            }
            MsgDispatcher.Broadcast(MsgEventType.Level_UpdateHeroHpSp);
        }

        public void RevivePlayer(long pid)
        {
            if (levelPlayer.id == pid)
            {
                //英雄退出待命状态
                regionCtrl.interactCtrl.QuitStandby();
                //处理数据
                levelPlayer.OnRevive();
            }
            //处理表现
            regionCtrl.entityCtrl.PlayRevive(pid);
        }

        /// <summary>
        /// 切换位面
        /// </summary>
        public async UniTask<bool> SwitchPhasing(GameScenePhasing phasing, Vector3 mainPos = default)
        {
            var logicPos = GameMathUtil.UnityPos2LogicPosInt(mainPos);
            var result = await CLevelLogicModule.NetCore.CS_SwitchPhasing(phasing, logicPos.x, logicPos.y, logicPos.z);
            if (result.IsFail)
            {
                return false;
            }
            var mainChar = regionCtrl.entityCtrl.GetMainCharacter();
            var dyn = mainChar.dynEntity;
            dyn.gameScenePhasing = phasing;
            return true;
        }

        #region lifecycle
        public void Update(float deltaTime, float unscaledDeltaTime)
        {
            if (regionCtrl != null)
            {
                regionCtrl.Update(deltaTime);
            }
        }

        protected override void OnDispose()
        {
            MsgDispatcher.RemoveListener<EnterLevelPOD>(MsgEventType.Level_Enter, NotifyEnterLevel);
            MsgDispatcher.RemoveListener<LevelRegionPOD>(MsgEventType.Level_EnterRegion, NotifyEnterRegion);
            MsgDispatcher.RemoveListener<int, int>(MsgEventType.Level_NotifyLevelParamChange, NotifyLevelParamChange);
            MsgDispatcher.RemoveListener<int>(MsgEventType.Level_NotifyHeroFollowerChange, NotifyHeroFollowerChange);
            MsgDispatcher.RemoveListener<int>(MsgEventType.Level_NotifyLevelTimeShowChange, OnNotifyLevelTimeShowChange);
            ManagerCenter.MonoDriver.updateEvent -= Update;
            LeaveRegion();
            permanentResource?.Dispose();
            permanentResource = null;

            levelPlayer = null;
            players.Clear();

            isLocal = false;
            cfgLevel = null;
            levelId = 0;
            customTimeHour = -1;
            levelParams?.Clear();
            levelParams = null;
            isLevelPlayStarted = false;
        }
        #endregion
    }
}
